﻿namespace My.Hydrator.Configuration
{
    using System;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using My.Hydrator.Formatters;
    using My.Hydrator.Helpers;

    public class DataTypeExpression
    {
        private readonly DataTypeAttribute dataTypeAttribute;

        public DataTypeExpression(DataType dataType)
        {
            this.dataTypeAttribute = new DataTypeAttribute(dataType);
        }

        public DataTypeExpression(string customDataType)
        {
            this.dataTypeAttribute = new DataTypeAttribute(customDataType);
        }

        protected Container Container
        {
            get { return SessionHelper.Container; }
        }

        public void Use<TFormatter>() where TFormatter : IDataTypeFormatter, new()
        {
            var formatter = new TFormatter();

            if (formatter.FormatterDataType.DataType != this.dataTypeAttribute.DataType || formatter.FormatterDataType.CustomDataType != this.dataTypeAttribute.CustomDataType)
            {
                throw new ArgumentException();
            }

            this.AddOrReplaceFormatter(formatter);
        }

        public void Use(IDataTypeFormatter dataTypeFormatter)
        {
            if (dataTypeFormatter.FormatterDataType.DataType != this.dataTypeAttribute.DataType || dataTypeFormatter.FormatterDataType.CustomDataType != this.dataTypeAttribute.CustomDataType)
            {
                throw new ArgumentException();
            }

            this.AddOrReplaceFormatter(dataTypeFormatter);
        }

        public void UseNumeric(int minValue, int maxValue)
        {
            var formatter = new NumericDataTypeFormatter<int>(minValue, maxValue, this.dataTypeAttribute.CustomDataType);

            this.AddOrReplaceFormatter(formatter);
        }

        public void UseNumeric(double minValue, double maxValue)
        {
            var formatter = new NumericDataTypeFormatter<double>(minValue, maxValue, this.dataTypeAttribute.CustomDataType);

            this.AddOrReplaceFormatter(formatter);
        }

        public void UseNumeric(float minValue, float maxValue)
        {
            var formatter = new NumericDataTypeFormatter<float>(minValue, maxValue, this.dataTypeAttribute.CustomDataType);

            this.AddOrReplaceFormatter(formatter);
        }

        public void UseNumeric(decimal minValue, decimal maxValue)
        {
            var formatter = new NumericDataTypeFormatter<decimal>(minValue, maxValue, this.dataTypeAttribute.CustomDataType);

            this.AddOrReplaceFormatter(formatter);
        }

        private IDataTypeFormatter GetFormatter()
        {
            return this.Container.Formatters.SingleOrDefault(x => x.FormatterDataType.DataType == this.dataTypeAttribute.DataType && x.FormatterDataType.CustomDataType == this.dataTypeAttribute.CustomDataType);
        }

        private void AddOrReplaceFormatter(IDataTypeFormatter newDataTypeFormatter)
        {
            var formatterToReplace = this.GetFormatter();
            if (formatterToReplace != null)
            {
                this.Container.Formatters.Remove(formatterToReplace);
            }

            this.Container.Formatters.Add(newDataTypeFormatter);
        }
    }
}
